//////////////////////////////////////////////////////////////////////////
//
// This file is a part of XORT project.
//
// PROJECT:         Xort compiler execution subsystem
// FILE:            xobject.cpp - runtime user declared object implementation
// AUTHOR:          Vladimir Gumenuk
// DATE:            8-Dec-2012
// MODIFICATIONS:
//
//
//     Copyright(c) 2014, Vladimir Gumenuk
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification, 
//     are permitted provided that the following conditions are met :
//
//     1. Redistributions of source code must retain the above copyright notice, 
//        this list of conditions and the following disclaimer.
//     2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 
//        the following disclaimer in the documentation and / or other materials provided with the distribution.
//     3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse 
//        or promote products derived from this software without specific prior written permission.
//
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
//     OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
//     THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
//     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
//     OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
//     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
//     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
//     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include "xort.h"
#include "xobject.h"
#include "xruntime.h"
#include "xcode.h"

XObject::XObject( XRuntime *pRuntime )
{
    m_pRuntime = pRuntime;
}

XObject::~XObject()
{
    Uninit();
}

int XObject::Init( symbolref pSymbol, XortNativeInterface *pExternalObj )
{
    int iRet = XORT_OK;
    if( pSymbol->m_SymKind != sk_obj && pSymbol->m_SymKind != sk_typename )
        return ERR_INVALID_SYMBOL_KIND;

    m_dwType  = pSymbol->m_dwType;
    m_pSymbol = pSymbol;

    SymbolListInfo symListInfo;
    GetSymbolListInfo( m_pSymbol->m_listMembers, symListInfo );

    m_Variables.reserve( symListInfo.dwVariblesCount + 1 );
    m_Functions.reserve( symListInfo.dwFunctionsCount + 1 );
    dword dwVarsCount = 0;
    dword dwProcsCount = 0;

    SYMBOL_LIST::iterator theIter = m_pSymbol->m_listMembers.begin();
    symbolref pSym = (Symbol*)theIter;
    while( pSym )
    {
        if( pSym->m_SymKind == sk_var )
        {
            if( pSym->m_dwStorage != cSTORAGE_MEMBER )
                return ERR_INVALID_STORAGE;

            if( pSym->m_dwType == t_p ) {
                if( m_pSymbol->m_SymKind != sk_typename )
                    throw ERR_RAWPTR_TYPE_FOUND;
            }

            XortElement var;
            var.bType   = ( pSym->m_dwType > t_p ) ? t_p : pSym->m_dwType;
            var.bAccess = ( pSym->m_dwFlags & f_const ) ? usr_VarR : usr_VarRW;

            if( pSym->m_dwType > t_p )
            {
                SymbolRef varType = FindType( m_pRuntime->get_GlobalSymbolList(), pSym->m_dwType );
                if( varType.isNULL() )
                    return ERR_TYPE_NOT_FOUND;

                if( varType.m_dwType == t_string )
                {
                    if( pSym->m_dwLiteralIdx ) {
                        var._p = m_pRuntime->get_StringFromPool( pSym->m_dwLiteralIdx );
                        var._p->AddRef();
                    } else {
                        if( pSym->m_Value.bType != t_v ) { // handle case if user initialize object like "string s = NULL;"
                            if( pSym->m_Value.bType != t_p || pSym->m_Value._p != NULL )
                                return ERR_INVALID_TYPE;
                            var._p = NULL;
                        } else {    // create empty string in this case
                            iRet = m_pRuntime->createXObject( varType, NULL, &var._p );
                            if( iRet != XORT_OK ) {
                                var._p->Release();
                                var._p = NULL;
                                return iRet;
                            }
                        }
                    }
                } else { // build user complex type - object
                    if( pSym->m_Value.bType != t_v ) { // handle initialization with NULL
                        if( pSym->m_Value.bType != t_p || pSym->m_Value._p != NULL )
                            return ERR_INVALID_TYPE;
                        var._p = NULL;
                    } else {
                        if( varType.m_dwType != pSymbol->m_dwType ) { // prevent recursion in self pointed objects
                            xobject *pObj = NULL;
                            iRet = m_pRuntime->createXObject( varType, NULL, &pObj );
                            if( iRet != XORT_OK ) {
                                pObj->Release();
                                return iRet;
                            }
                            var._p = pObj;
                        } else {                // create NULL ptr instead
                            var._p = NULL;
                        }
                    }
                }

            } else {
                var._u8 = pSym->m_Value._u8;
            }

            m_Variables.put( (dword)pSym->m_iIndex, var );
            dwVarsCount++;
        }
        else
        if( pSym->m_SymKind == sk_proc )
        {
            iRet = xc_removeLabels( *pSym ); // remove all labels and optimize goto
            if( iRet != XORT_OK )
                return iRet;

            pSym->GenerateIPLineMap();
            m_Functions.put( (dword)pSym->m_iIndex, pSym );
            dwProcsCount++;
        }
        // else just skip unknown symbol

        theIter++;
        pSym = theIter == NULL ? NULL : (Symbol*)theIter;
    }

    m_Variables.set_itemsCount( dwVarsCount + 1 );
    m_Functions.set_itemsCount( dwProcsCount + 1 );

    return XORT_OK;
}

void XObject::Uninit()
{
    m_Functions.clear();
    m_Variables.clear();
    m_pSymbol = NULL;
    m_pRuntime = NULL;
}

// Invokes the object method
int XObject::Invoke( dword dwFuncIndex, XRuntime *pRuntime )
{
    if( dwFuncIndex > m_Functions.count() )
        return ERR_INVALID_METHOD_INDEX;

    symbolref pMethod = m_Functions.get( dwFuncIndex );

    return pRuntime->Exec( *pMethod );
}

// assigns new value to the internal variable
int XObject::Put( dword dwVarIndex, const XortElement& Value )
{
    if( dwVarIndex > m_Variables.count() )
        return ERR_INVALID_PROPERTY_INDEX;

    XortElement& Var = m_Variables.get( dwVarIndex );

    if( Var.bType != Value.bType )
        return ERR_TYPES_MISMATCH;
    if( Var.bAccess & f_const )
        return ERR_ASSIGN_TO_CONST_PROP;

    Var = Value;

    return XORT_OK;
}

// returns the value of the internal variable
int XObject::Get( dword dwVarIndex, XortElement& Value )
{
    if( dwVarIndex > m_Variables.count() )
        return ERR_INVALID_PROPERTY_INDEX;

    XortElement& Var = m_Variables.get( dwVarIndex );

    Value = Var;

    return XORT_OK;
}

symbolref XObject::get_Method( dword dwIdx )
{
    if( dwIdx > m_Functions.count() )
        return NULL;

    symbolref pMethod = m_Functions.get( dwIdx );

    return pMethod;
}

int XObject::Pack( void **ppOut ) // returns the ByteArray object with packed data
{
    int iRet = XORT_OK;

    return iRet;
}

int XObject::Unpack( void *pByteArray ) // accepts the ByteArray object with packed data 
{
    int iRet = XORT_OK;

    return iRet;
}